package com.example.demo.Service.lmp;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.Dto.ChargeDto;
import com.example.demo.Dto.TransactionInfoDto;
import com.example.demo.Enum.TransactionStatusEnum;
import com.example.demo.Mapper.TransactionMapper;
import com.example.demo.Service.*;
import com.example.demo.Util.ThreadLocallUtil;
import com.example.demo.Util.ThreadPoolUtil;
import com.example.demo.VO.ChargeVo;
import com.example.demo.VO.TransactionInfoVo;
import com.example.demo.po.Credit;
import com.example.demo.po.Order;
import com.example.demo.po.OrderItem;
import com.example.demo.po.Transaction;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Slf4j
public class TransactionServiceImp extends ServiceImpl<TransactionMapper, Transaction> implements TransactionSrevice {
    @Value("${wait_Time}")
    private  String Wait_Time;
    private final RedisTemplate< String, Object> redisTemplate;
    private final CheckoutService checkoutService;
    private final OrderService orderService;
    private final OrderItemService orderItemService;
    private final CreditService creditService;
    @Override
    public TransactionInfoVo getTransaction(TransactionInfoDto transactionInfoDto) {
        if (transactionInfoDto.getTransactionId() != null) {
            Transaction byId = getById(transactionInfoDto.getTransactionId());
            return BeanUtil.toBean(byId, TransactionInfoVo.class);
        }
        if (transactionInfoDto.getPreTransactionId() != null) {
            Transaction preTransactionId = getOne(new QueryWrapper<Transaction>().eq("pre_transaction_id", transactionInfoDto.getPreTransactionId()));
            return BeanUtil.toBean(preTransactionId, TransactionInfoVo.class);
        }
        throw new RuntimeException("参数错误");
    }

    @Override
    public IPage<TransactionInfoVo> getTransactionList(String pageNum, String pageSize) {
        Page<Transaction> page = new Page<>(Integer.parseInt(pageNum), Integer.parseInt(pageSize));
        // 执行查询并分页，移除多余的query().page()调用
        IPage<Transaction> resultPage = lambdaQuery()
                .eq(Transaction::getUserId, ThreadLocallUtil.getUser().getUserId())
                .page(page);
        IPage<TransactionInfoVo> page1 = (IPage<TransactionInfoVo>) resultPage.convert(item -> BeanUtil.toBean(item, TransactionInfoVo.class));
        return page1;
    }

    @Override
    @Transactional
    public ChargeVo ChargeByOrderIdAndCreditId(ChargeDto chargeDto) {
        // 上锁
    if(redisTemplate.opsForValue().setIfAbsent("上锁orderId:" + chargeDto.getOrderId(), ThreadLocallUtil.userThreadLocal)){
            try {
                Transaction orderId = getOne(new QueryWrapper<Transaction>().eq("order_id", chargeDto.getOrderId()));
                if (orderId != null){
                    throw new RuntimeException("订单已存在");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            List<OrderItem> orderId = orderItemService.list(new QueryWrapper<OrderItem>().eq("order_id", chargeDto.getOrderId()));
            double sum = orderId.stream().mapToDouble(OrderItem::getCost).sum();
            Transaction transaction = Transaction.builder()
                    .preTransactionId(UUID.randomUUID().toString())
                    .userId(ThreadLocallUtil.getUser().getUserId())
                    .orderId(chargeDto.getOrderId())
                    .creditId(chargeDto.getCreditId())
                    .amount((float) sum)
                    .status(TransactionStatusEnum.WAIT_FOR_CONFIRM)
                    .createTime(LocalDateTime.now())
                    .build();
            if (!(save(transaction))){
                throw new RuntimeException("添加失败");
            }
            //使用线程池定时任务，也可以自定义rabbitmq 实现定时任务
            ThreadPoolUtil.Scheruled(() -> {
                Transaction transactionId = getOne(new QueryWrapper<Transaction>().eq("transaction_id", transaction.getTransactionId()));
                TransactionStatusEnum status = transactionId.getStatus();
                if (!(status==TransactionStatusEnum.PAY_SUCCESS)) {
                    deletepaymenys(transaction.getPreTransactionId());
                }
            }, Long.parseLong(Wait_Time));
            ChargeVo chargeVo = new ChargeVo();
            chargeVo.setTransactionId(transaction.getTransactionId());
            chargeVo.setPreTransactionId(transaction.getPreTransactionId());
            return chargeVo;
        }
        log.info("上锁失败");
        throw new RuntimeException("频繁点击");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    // 取消支付
    public void deletepaymenys(String preTransactionId) {
        LocalDateTime now = LocalDateTime.now();
        try {
          update().eq("pre_transaction_id", preTransactionId)
                  .set("status", TransactionStatusEnum. CANCELED)
                  .set( "update_time", now)
                  .set("deleted", 1);
          Transaction preTransactionId1 = getOne(new QueryWrapper<Transaction>()
                  .eq("pre_transaction_id", preTransactionId));
          String transactionId = preTransactionId1.getTransactionId();
          String orderId = preTransactionId1.getOrderId();
          checkoutService.update().eq("transaction_id", transactionId)
                  .set("status", 3).
                  set( "update_time", now)
                  .set("deleted", 1);
          orderService.update(new UpdateWrapper<Order>().eq("order_id", orderId).set("status", 3)
                  .set("update_time", now)
                  .set("deleted", 1));
          orderItemService.update().eq("order_id", orderId).set("status", 3).set("update_time", now);
      }
      catch (Exception e){
          throw new RuntimeException("取消支付失败");
      }
    }

    @Override
    @Transactional
    // 确认支付
    public void trueTranssation(String preTransactionId) {
        LocalDate.now()
        update().eq("pre_transaction_id", preTransactionId)
                .set("status", TransactionStatusEnum.PAY_SUCCESS)
                .set( "update_time", LocalDateTime.now());
        Transaction preTransactionId1 = getOne(new QueryWrapper<Transaction>().eq("pre_transaction_id", preTransactionId));
        if (preTransactionId1 == null){
            throw new RuntimeException("参数错误");
        }
        String creditId = preTransactionId1.getCreditId();
        Float amount = preTransactionId1.getAmount();
        String orderId = preTransactionId1.getOrderId();
        checkoutService.update().eq("transaction_id", preTransactionId1.getTransactionId())
                .set("status", 3).
                set( "update_time", LocalDateTime.now());
        orderService.update(new UpdateWrapper<Order>().eq("order_id", orderId).set("status", 2)
                .set("update_time", LocalDateTime.now()));
        orderItemService.update().eq("order_id", orderId).set("status", 2).set("update_time", LocalDateTime.now());
        Credit cardNumber = creditService.getOne(new QueryWrapper<Credit>().eq("card_number", creditId));
        if (cardNumber.getExpire_date().isBefore(LocalDate.now())) {
            // 信用卡已过期
            throw new RuntimeException("信用卡已过期");
        }
        Double balance = cardNumber.getBalance();
        balance -= amount;
        if ((balance<0)){
            throw new RuntimeException("余额不足");
        }
        creditService.update().eq("credit_id", creditId)
                .set( "update_time", LocalDateTime.now())
                .set("balance", balance);
    }
}
